/*
 * Copyright (c) 2023 fred
 * Licensed under the Apache License,Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */
import { Validate } from '../helper/Validate';
import constants from '../util/Constants';
import { Node } from './Node';
import { Position } from './Position';

export class Range {

  private readonly _start:Position
  private readonly _end:Position
  private static readonly _RangeKey = constants.internalKey("jsoup.sourceRange")
  private static readonly _EndRangeKey = constants.internalKey("jsoup.endSourceRange")
  private static _UntrackedPos = new Position(-1,-1,-1)
  private static readonly _Untracked = new Range(Range._UntrackedPos,Range._UntrackedPos)

  constructor(start:Position,end:Position) {
    this._start = start
    this._end = end
  }

  /**
   Get the start position of this node.
   * @return the start position
   */
  public getStart():Position {
    return this._start
  }

  /**
   Get the end position of this node.
   * @return the end position
   */
  public getEnd():Position {
    return this._end
  }

  /**
   Test if this source range was tracked during parsing.
   * @return true if this was tracked during parsing, false otherwise (and all fields will be {@code -1}).
   */
  public isTracked():boolean {
    return this !== Range._Untracked
  }

  /**
   Retrieves the source range for a given Node.
   * @param node the node to retrieve the position for
   * @param start if this is the starting range. {@code false} for Element end tags.
   * @return the Range, or the Untracked (-1) position if tracking is disabled.
   */
  static of(node: Node, start: boolean): Range {
    let key = start ? Range._RangeKey : Range._EndRangeKey;
    if (!node.hasAttr(key)){
      return this._Untracked;
    } else {
      return Validate.ensureNotNull(node.attributes().getUserData(key)) as Range
    }
  }

  /**
   Internal jsoup method, called by the TreeBuilder. Tracks a Range for a Node.
   * @param node the node to associate this position to
   * @param start if this is the starting range. {@code false} for Element end tags.
   */
  public track(node:Node, start:boolean) {
    node.attributes().putUserData(start?Range._RangeKey:Range._EndRangeKey,this)
  }

  public equals(o:any):boolean {
    if (this === o) {
      return true
    }
    if (o ===undefined || o === null) {
      return false
    }
    if (!(o instanceof Range)) {
      return false
    }
    let range = o as Range
    if (!this._start.equals(range._start)) {
      return false
    }
    return this._end.equals(range._end)
  }

  public hashCode():number {
    let result = this._start.hashCode()
    result = 31*result + this._end.hashCode()
    return result
  }

  /**
   Gets a String presentation of this Range, in the format {@code line,column:pos-line,column:pos}.
   * @return a String
   */
  public toString():string {
    return `${this._start} - ${this._end}`
  }

}

